1
2
3
4 package uk.ac.roe.antigen.builder;
5
6 import java.io.BufferedInputStream;
7 import java.io.File;
8 import java.io.FileInputStream;
9 import java.io.FileNotFoundException;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.net.MalformedURLException;
13 import java.net.URL;
14 import java.net.URLDecoder;
15 import java.util.Enumeration;
16 import java.util.Hashtable;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Properties;
20 import java.util.logging.Handler;
21 import java.util.logging.Level;
22 import java.util.logging.Logger;
23
24 import org.apache.commons.cli.CommandLine;
25 import org.apache.commons.cli.HelpFormatter;
26 import org.apache.commons.cli.Option;
27 import org.apache.commons.cli.OptionBuilder;
28 import org.apache.commons.cli.Options;
29 import org.apache.commons.cli.ParseException;
30 import org.apache.commons.cli.Parser;
31 import org.apache.commons.cli.PosixParser;
32 import org.apache.tools.ant.BuildException;
33 import org.apache.tools.ant.BuildLogger;
34 import org.apache.tools.ant.DefaultLogger;
35 import org.apache.tools.ant.Project;
36 import org.apache.tools.ant.ProjectHelper;
37 import org.apache.tools.ant.Target;
38 import org.apache.tools.ant.input.InputHandler;
39 import org.apache.velocity.exception.MethodInvocationException;
40 import org.apache.velocity.exception.ParseErrorException;
41 import org.apache.velocity.exception.ResourceNotFoundException;
42
43 import uk.ac.roe.antigen.ant.BuildGrabber;
44 import uk.ac.roe.antigen.ant.GeneralInputHandler;
45 import uk.ac.roe.antigen.ant.TaskTreeCalcuator;
46 import uk.ac.roe.antigen.dialogs.AbortDialog;
47 import uk.ac.roe.antigen.dialogs.BuildLoggerFrame;
48 import uk.ac.roe.antigen.dialogs.InfoFrame;
49 import uk.ac.roe.antigen.dialogs.SplashScreen;
50 import uk.ac.roe.antigen.dialogs.TargetChoiceFrame;
51 import uk.ac.roe.antigen.textcomponents.DefaultPromptingInputHandler;
52 import uk.ac.roe.antigen.textcomponents.TextInfoDisplay;
53 import uk.ac.roe.antigen.textcomponents.TextTargetChooser;
54 import uk.ac.roe.antigen.utils.Config;
55 import uk.ac.roe.antigen.utils.CopyableFile;
56
57 /***
58 * Main controller, loads up the build file, and fires it at Ant, setting up our
59 * Custom loggers and inputhandlers.
60 *
61 * @todo refactor and make it less embarrassing
62 *
63 * @author jdt
64 */
65 public class Installer {
66
67 private static final String ANTIGEN_HOME = "antigen.home";
68
69 private static final String ANTIGEN_JARFILE = "antigen.jarfile";
70
71 /***
72 * Logger for this class
73 */
74 private static final Logger logger = Logger.getLogger(Installer.class.getName());
75
76 private static final String PROPERTY_OPTION = "D";
77
78 private static final String PROXY_OPTION="p";
79
80 private static final String PROPERTYFILE_OPTION = "f";
81
82 private static final String CMDLINE_OPTION = "c";
83
84 private static final String HELP_OPTION = "h";
85
86 private static final String DEBUG_OPTION = "d";
87
88 private static final String PROXYUSER_OPTION = "u";
89
90 private static final String PROPERTY_ANTIGEN_OPTION = "A";
91
92 public static void main(String[] args) throws Throwable {
93
94
95 CommandLine line = parseCommandLine(args);
96
97 Config.load("/config.properties");
98
99
100 if (line.hasOption(PROPERTY_ANTIGEN_OPTION)) {
101 String[] keyValuePairs=line.getOptionValues(PROPERTY_ANTIGEN_OPTION);
102 logger.fine("Found "+keyValuePairs.length/2+" antigen properties");
103 for (int i=0;i<keyValuePairs.length/2;++i) {
104 String key=keyValuePairs[2*i];
105 String value=keyValuePairs[2*i+1];
106 logger.fine("Overriding property "+key+"="+value);
107 Config.setProperty(key,value);
108 }
109 }
110
111 String levelString = Config.getProperty("antigen.loglevel","SEVERE");
112 boolean debug = line.hasOption(DEBUG_OPTION);
113 if (debug) {
114 levelString = line.getOptionValue(DEBUG_OPTION);
115 }
116 Level logLevel = Level.parse(levelString);
117
118
119
120 Logger rootLogger = Logger.getLogger("");
121 rootLogger.setLevel(logLevel);
122 Handler[] handlers = rootLogger.getHandlers();
123 assert handlers.length != 0;
124 Handler handler = handlers[0];
125 handler.setLevel(logLevel);
126 logger.info("Debug mode: "+levelString);
127
128 boolean textOnly = line.hasOption(CMDLINE_OPTION);
129 logger.info("Text only mode: " + textOnly);
130
131 Properties sysprops = System.getProperties();
132 if (line.hasOption(PROXY_OPTION)) {
133 logger.fine("Setting proxy");
134 String hostAndPort = line.getOptionValue(PROXY_OPTION);
135 String[] hostAndPortSplit = hostAndPort.split(":");
136 String host = hostAndPortSplit[0];
137 String port = hostAndPortSplit.length>1 ? hostAndPortSplit[1] : "80";
138 logger.fine("Proxying through "+host+" on port "+port);
139
140
141 sysprops.put("http.proxyHost", host);
142 sysprops.put("http.proxyPort", port);
143 if (line.hasOption(PROXYUSER_OPTION)) {
144 String userAndPass = line.getOptionValue(PROXYUSER_OPTION);
145 String[] userAndPassSplit = userAndPass.split(":");
146 String proxyUser = userAndPassSplit[0];
147 String proxyPassword = userAndPassSplit.length>1 ? userAndPassSplit[1] : "";
148 sysprops.put("http.proxyUser", proxyUser);
149 sysprops.put("http.proxyPassword", proxyPassword);
150 }
151 }
152
153 File thisJar;
154 logger.fine("Getting this jar");
155 try {
156 thisJar = new Installer().getEnclosingJar();
157 } catch (FileNotFoundException fe) {
158 logger.warning(fe.getMessage());
159 logger.warning("Unable to set property "+ANTIGEN_JARFILE);
160 thisJar = new File("Unknown");
161 }
162
163
164 if ((Config.getProperty("antigen.splash.logo")!=null || Config.getProperty("antigen.splash.text")!=null) && !textOnly) {
165 new SplashScreen().show();
166 }
167
168
169
170
171
172
173
174 String buildSrcType = Config.getProperty("build.src.type");
175 String buildSrcLoc = Config.getProperty("build.src.location");
176 assert buildSrcLoc != null;
177 CopyableFile tmpDir = null;
178
179 String buildDir = Config.getProperty("antigen.tmp.dir");
180 try {
181 BuildGrabber buildGrabber;
182 if (buildDir!=null) {
183 buildGrabber= new BuildGrabber(new File(buildDir));
184 } else {
185 buildGrabber= new BuildGrabber();
186 }
187 if ("dir".equals(buildSrcType)) {
188
189 logger.fine("Grabbing from directory " + buildSrcLoc);
190 tmpDir = buildGrabber.grab(new File(buildSrcLoc));
191 } else if ("classpath".equals(buildSrcType)) {
192
193
194 logger.fine("Grabbing from the classpath off " + buildSrcLoc);
195 tmpDir = buildGrabber.grab(buildSrcLoc);
196 } else if ("self".equals(buildSrcType)) {
197 logger.fine("Grabbing from self");
198 tmpDir = buildGrabber.grab(thisJar.toURL());
199 } else {
200
201 tmpDir = buildGrabber.grab(new URL(buildSrcLoc));
202 }
203 } catch (MalformedURLException e1) {
204 logger.severe("Unable to load build script from " + buildSrcType + " " + buildSrcLoc);
205 logger.severe("Sorry, but I can't continue");
206 logger.fine(e1.getMessage());
207 throw e1;
208 } catch (IOException e1) {
209 logger.severe("Unable to load build script from " + buildSrcType + " " + buildSrcLoc);
210 logger.severe("Sorry, but I can't continue");
211 logger.fine(e1.getMessage());
212 throw e1;
213 }
214
215 BuildLoggerFrame builddialog = null;
216 if (!textOnly) {
217 builddialog = new BuildLoggerFrame();
218 }
219 showInfoDialog(builddialog, sysprops, "antigen.intro.text", "antigen.intro.template", textOnly);
220
221 File buildFile = new File(tmpDir, "build.xml");
222
223 Project project = new Project();
224 project.init();
225 ProjectHelper.configureProject(project, buildFile);
226
227
228
229 project.setProperty(ANTIGEN_HOME, System.getProperty("user.dir"));
230 project.setProperty(ANTIGEN_JARFILE, thisJar.getAbsolutePath());
231
232
233
234 if (line.hasOption(PROPERTY_OPTION)) {
235 String[] keyValuePairs=line.getOptionValues(PROPERTY_OPTION);
236 logger.fine("Found "+keyValuePairs.length/2+" command line properties");
237 for (int i=0;i<keyValuePairs.length/2;++i) {
238 String key=keyValuePairs[2*i];
239 String value=keyValuePairs[2*i+1];
240 logger.fine("Setting property "+key+"="+value);
241 project.setProperty(key, value);
242 }
243 }
244
245
246 if (line.hasOption(PROPERTYFILE_OPTION)) {
247 logger.fine("Attempting to load properties from a file");
248 File inputFile = new File(line.getOptionValue(PROPERTYFILE_OPTION));
249
250
251 Properties props = new Properties();
252 InputStream is = null;
253 try {
254 is = new BufferedInputStream(new FileInputStream(inputFile));
255 props.load(is);
256 } catch (IOException ie) {
257 logger.warning("Was unable to load properties file " + ie.getMessage() + "...continuing");
258 } finally {
259 if (is != null)
260 try {
261 is.close();
262 } catch (IOException swallow) {
263
264 }
265 }
266 Enumeration enumer = props.keys();
267 while (enumer.hasMoreElements()) {
268 String key = (String) enumer.nextElement();
269 logger.fine("Putting property " + key + "=" + props.getProperty(key));
270 project.setProperty(key, props.getProperty(key));
271 }
272 }
273
274
275 String targetName = project.getDefaultTarget();
276
277 String anyChosenTargets = Config.getProperty(TargetChoiceFrame.ANTIGEN_TARGETS);
278 if (anyChosenTargets != null) {
279 if (anyChosenTargets.split(",").length == 1) {
280
281 targetName = anyChosenTargets;
282
283 } else {
284 TargetChooser targetChoiceFrame;
285 if (textOnly) {
286 targetChoiceFrame = new TextTargetChooser(project);
287 } else {
288 targetChoiceFrame = new TargetChoiceFrame(project);
289 }
290 List targetNames = targetChoiceFrame.showAndWaitForResponse();
291 Target master = new Target();
292
293
294 Iterator it = targetNames.iterator();
295 while (it.hasNext()) {
296 String next = (String) it.next();
297 logger.fine("Adding target " + next);
298 master.addDependency(next);
299 }
300 final String masterTargetName = "master";
301 master.setName(masterTargetName);
302 master.setDescription("One project to rule them all");
303
304 project.addTarget(masterTargetName, master);
305 targetName = masterTargetName;
306 project.init();
307 }
308 }
309
310
311
312 int numberOfTasks = new TaskTreeCalcuator(project).getNumberOfTasksToRun(targetName);
313 logger.fine("main(String[])" + numberOfTasks);
314
315 InputHandler inputHandler;
316 BuildLogger buildLogger;
317 if (!textOnly) {
318 builddialog.setVisible(true);
319 builddialog.setNumberOfTasks(numberOfTasks);
320
321 BuildLogger dialogBuildLogger = builddialog.getBuildLogger();
322
323 inputHandler = new GeneralInputHandler(builddialog);
324 buildLogger = dialogBuildLogger;
325
326 } else {
327 inputHandler = new DefaultPromptingInputHandler();
328 DefaultLogger defaultListener = new DefaultLogger();
329 buildLogger = defaultListener;
330 buildLogger.setOutputPrintStream(System.out);
331 buildLogger.setErrorPrintStream(System.err);
332 }
333
334 int antOutputLevel = Integer.parseInt(Config.getProperty("antigen.antoutput","2"));
335 buildLogger.setMessageOutputLevel(antOutputLevel);
336 project.addBuildListener(buildLogger);
337 project.setInputHandler(inputHandler);
338
339 try {
340 project.executeTarget(targetName);
341 if (!textOnly) {
342 builddialog.setFinished();
343 }
344
345 showInfoDialog(builddialog, project.getProperties(), "antigen.outro.text", "antigen.outro.template",
346 textOnly);
347
348 } catch (BuildException e) {
349 logger.info("There was an error in the build process");
350 logger.info("e.getClass()");
351 logger.info("e");
352 if (textOnly) {
353 System.out.println(e.getMessage());
354 } else {
355 new AbortDialog(builddialog, e.getMessage()).show();
356 }
357 } finally {
358
359 if (buildDir==null) {
360 tmpDir.recursivelyDelete();
361 }
362 }
363
364 }
365
366 /***
367 * @param args
368 */
369 private static CommandLine parseCommandLine(String[] args) {
370 Parser parser = new PosixParser();
371 Options options = new Options();
372 options.addOption(HELP_OPTION, "help", false, "Display this help.")
373 .addOption(CMDLINE_OPTION, "commandline", false, "Execute on the command line (without a GUI)")
374 .addOption(DEBUG_OPTION, "debug", true,"Set the commandline debug output level - see java doc for java/util/logging/Level for values, defaults to SEVERE")
375 .addOption(PROPERTYFILE_OPTION, "properties", true,"Supply a .property file")
376
377 .addOption(PROXY_OPTION,"proxy",true,"Supply a web proxy host (host:port)")
378 .addOption(PROXYUSER_OPTION,"user",true, "Supply a user name for the proxy (user:password)");
379 Option propertyOpt = OptionBuilder.withArgName( "property=value" )
380 .withLongOpt("antproperty")
381 .hasArg()
382 .withValueSeparator()
383 .withDescription( "use value for given property in the Ant build" )
384 .create( PROPERTY_OPTION );
385 options.addOption(propertyOpt);
386 propertyOpt.setArgs(Option.UNLIMITED_VALUES);
387
388 Option antigenPropertyOpt = OptionBuilder.withArgName( "property=value" )
389 .withLongOpt("antigenproperty")
390 .hasArg()
391 .withValueSeparator()
392 .withDescription( "use value for given property, overriding the value in the Antigen config.properties file" )
393 .create( PROPERTY_ANTIGEN_OPTION );
394 options.addOption(antigenPropertyOpt);
395 antigenPropertyOpt.setArgs(Option.UNLIMITED_VALUES);
396
397 CommandLine line = null;
398 try {
399 line = parser.parse(options, args);
400 } catch (ParseException e) {
401 logger.severe("Error parsing command line " + e.getMessage());
402 displayHelp(options);
403 System.exit(1);
404 }
405 if (line.hasOption(HELP_OPTION)) {
406 displayHelp(options);
407 System.exit(0);
408 }
409 return line;
410 }
411
412 /***
413 * @param options
414 *
415 */
416 private static void displayHelp(Options options) {
417 new HelpFormatter().printHelp("java -jar (jarname) option", options);
418 }
419
420 /***
421 * @param builddialog
422 * @param properties
423 * @param textKey
424 * @param templateKey
425 * @param textOnly
426 * @throws Exception
427 * @throws ResourceNotFoundException
428 * @throws ParseErrorException
429 * @throws MethodInvocationException
430 */
431 private static void showInfoDialog(BuildLoggerFrame builddialog, Hashtable properties, String textKey,
432 String templateKey, boolean textOnly) throws Exception {
433 String text = Config.getProperty(textKey);
434 String template = Config.getProperty(templateKey);
435 String message = template != null ? MessageProcessor.processMessage(properties, template) : text;
436
437 if (message == null) {
438 logger.fine("No information text");
439 return;
440 }
441
442 InfoDisplay display;
443 if (textOnly) {
444 display = new TextInfoDisplay(message);
445 } else {
446 display = new InfoFrame(message);
447 }
448
449 display.showAndWaitForResponse();
450 }
451
452 /***
453 * Get the File that this jar is being run from. Lifted from antinstaller's
454 * SelfExtractor http://antinstaller.sourceforge.net and modified a bit
455 *
456 * @return
457 * @throws FileNotFoundException
458 */
459
460 private File getEnclosingJar() throws FileNotFoundException {
461 logger.fine("Attempting to get enclosing jar name");
462 String thisClass = "/" + getClass().getName().replace('.', '/') + ".class";
463 logger.finer("This class " + thisClass);
464 URL jarUrl = this.getClass().getResource(thisClass);
465 String stringForm = jarUrl.toString();
466 logger.finer("Jar URL " + stringForm);
467 String fileForm = jarUrl.getFile();
468
469 int endIdx = stringForm.indexOf("!/");
470 if (endIdx == -1) {
471 throw new FileNotFoundException("Could not locate enclosing jar: !/ not found in URL");
472 }
473
474 String fileNamePart = stringForm.substring("jar:file:".length(), endIdx);
475 logger.finer("Extracted file name: " + fileNamePart);
476 String unescaped = URLDecoder.decode(fileNamePart);
477 logger.finer("Unescaped Extracted file name: " + unescaped);
478 File file = new File(unescaped);
479 if (!file.exists()) {
480 throw new FileNotFoundException("Could not locate enclosing jar: file "+file+" not found");
481 }
482 return file;
483 }
484
485 }